package org.centsuse.intention_spring.invention.service.impl;

import dev.langchain4j.model.chat.ChatLanguageModel;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.centsuse.intention_spring.invention.config.scenes.SceneConfig;
import org.centsuse.intention_spring.invention.config.scenes.processor.SceneProcessor;
import org.centsuse.intention_spring.invention.entities.contexts.ContextManager;
import org.centsuse.intention_spring.invention.entities.contexts.DialogueContext;
import org.centsuse.intention_spring.invention.entities.http.ChatRequest;
import org.centsuse.intention_spring.invention.entities.http.ChatResponse;
import org.centsuse.intention_spring.invention.factory.ModelFactory;
import org.centsuse.intention_spring.invention.factory.SceneFactory;
import org.centsuse.intention_spring.invention.service.Chat2AIService;
import org.centsuse.intention_spring.invention.utils.DataformatUtils;
import org.centsuse.intention_spring.invention.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Service
@Data
public class Chat2AIServiceImpl implements Chat2AIService {

    /** 相似度 */
    private static final float RELATED_INTENT_THRESHOLD = 0.5f; // 从配置读取

    /** 记录会话sessionId */
    private static final ThreadLocal<String> sessionHolder = new ThreadLocal<>();

    @Autowired
    private ContextManager contextManager;

    @Autowired
    private ModelFactory modelFactory;

    @Autowired
    private SceneFactory sceneFactory;

    @Override
    public boolean isRelatedToLastIntent(ChatRequest request) {
        log.info("[Chat2AIServiceImpl][isRelatedToLastIntent], request: {}", request);
        // 传入sessionId，并放入线程对象中，如果没有传入sessionId，则生成一个，根据sessionId获取历史会话内容包括槽值等
        generateSessionId(request.getSessionId());
        // 获取当前上下文
        DialogueContext context = contextManager.getContext(sessionHolder.get());
        String currentPurpose = context.getCurrentPurpose();
        if (currentPurpose.isEmpty()) {
            return false;
        }
        Map<String, SceneConfig> sceneConfigMap = sceneFactory.getSceneConfiguration().getSceneConfigMap();
        String currentSceneDesc = sceneConfigMap.get(currentPurpose).getDescription();
        String prompt = String.format(
                "判断当前用户输入内容与当前对话场景的关联性:%n%n当前对话场景: %s%n当前用户输入: %s%n%n这两次输入是否关联（仅用小数回答关联度，得分范围0.0至1.0）",
                currentSceneDesc, request.getInput()
        );
        log.info("[Chat2AIServiceImpl][isRelatedToLastIntent], prompt: {}", prompt);
        ChatLanguageModel chatLanguageModel = getLanguageModel();
        String result = chatLanguageModel.chat(prompt);
        log.info("[Chat2AIServiceImpl][isRelatedToLastIntent], result: {}", result);
        return DataformatUtils.extractFloat(result) > RELATED_INTENT_THRESHOLD;
    }

    @Override
    public String recognizeIntent(ChatRequest request) {
        log.info("[Chat2AIServiceImpl][recognizeIntent], start recognizeIntent, request: {}", request);
        StringBuilder optionsPrompt = new StringBuilder();
        Map<String, String> purposeOptions = new LinkedHashMap<>();
        int index = 1;
        Map<String, SceneConfig> sceneConfigMap = sceneFactory.getSceneConfiguration().getSceneConfigMap();
        for (Map.Entry<String, SceneConfig> entry : sceneConfigMap.entrySet()) {
            String key = String.valueOf(index);
            SceneConfig sceneConfig = entry.getValue();
            if (null == sceneConfig) {
                continue;
            }
            purposeOptions.put(key, entry.getKey());
            optionsPrompt.append(String.format("%s. %s - 请回复%s%n", key, sceneConfig.getDescription(), key));
            index++;
        }
        optionsPrompt.append("0. 其他场景 - 请回复0");
        log.info("[Chat2AIServiceImpl][recognizeIntent], optionsPrompt: {}", optionsPrompt);

        String fullPrompt = String.format(
                "有下面多种场景，需要你根据用户输入进行判断，只答选项%n%s%n用户输入：%s%n请回复序号：",
                optionsPrompt, request.getInput()
        );
        log.info("[Chat2AIServiceImpl][recognizeIntent], fullPrompt: {}", fullPrompt);
        ChatLanguageModel chatLanguageModel = getLanguageModel();
        String userChoice = chatLanguageModel.chat(fullPrompt);
        log.info("[Chat2AIServiceImpl][recognizeIntent], purposeOptions: {}, userChoice: {}", purposeOptions, userChoice);

        String cleanChoice = DataformatUtils.extractAfterLastThink(userChoice);
        List<String> userChoices = DataformatUtils.extractContinuousDigits(cleanChoice);
        // 获取当前上下文
        DialogueContext context = contextManager.getContext(sessionHolder.get());
        String currentPurpose = context.getCurrentPurpose();
        if (!userChoices.isEmpty() && !"0".equals(userChoices.get(0))) {
            currentPurpose = purposeOptions.getOrDefault(userChoices.get(0), "");
        }
        Map<String, SceneConfig> configMap = this.sceneFactory.getSceneConfiguration().getSceneConfigMap();
        if (!currentPurpose.isEmpty() && null != configMap.get(currentPurpose)) {
            log.info("[Chat2AIServiceImpl][recognizeIntent], 用户选择了场景 {}", configMap.get(currentPurpose).getName());
            // 更新会话场景意图和槽值
            context.updateCurrentPurpose(currentPurpose);
            context.initSlot(configMap.get(currentPurpose));
            context.initChatMemory();
            return currentPurpose;
        } else {
            log.info("[Chat2AIServiceImpl][recognizeIntent], 无效的选项，请重新选择。");
            return "-1";
        }
    }

    @Override
    public ChatResponse processMultiQuestion(ChatRequest request) {
        log.info("[Chat2AIServiceImpl][processMultiQuestion] request is {}, contextManager is {}", request, contextManager);
        generateSessionId(request.getSessionId());
        try {
            if (!isRelatedToLastIntent(request)) {
                recognizeIntent(request);
            }
            DialogueContext context = contextManager.getContext(sessionHolder.get());
            String currentPurpose = context.getCurrentPurpose();
//            context.initSlot(sceneFactory.getSceneConfiguration().getSceneConfigMap().get(currentPurpose));
            Map<String, SceneConfig> configMap = this.sceneFactory.getSceneConfiguration().getSceneConfigMap();
            // 如果场景模板中包含当前的意图场景
            // 获取场景执行器进行处理
            if (configMap.containsKey(currentPurpose)) {
                SceneProcessor processor = getProcessorForScene(currentPurpose);
                return processor.process(request.getInput(), context);
            }
            // 定义结果类 TODO
            return new ChatResponse("未命中场景", context.getSessionId());
        } catch (Exception e) {
            log.error("[Chat2AIServiceImpl][processMultiQuestion], 场景处理异常: e", e);
            return new ChatResponse("场景处理异常", sessionHolder.get());
        }
    }

    /**
     * @description 根据名称获取场景处理器
     * @param sceneName 场景名称
     * @return SceneProcessor 场景处理器
     * @throws Exception 异常
     */
    private SceneProcessor getProcessorForScene(String sceneName) throws Exception {
        String processorName = this.sceneFactory.getSceneConfiguration().getSceneConfigMap().get(sceneName).getProcessorClass();
        SceneProcessor processor = sceneFactory.getProcessor(processorName);
        if (null == processor) {
            throw new IllegalArgumentException("未找到名为" + sceneName + "的场景配置");
        }
        log.info("[Chat2AIServiceImpl][recognizeIntent], sceneName is {}, processor is {}", sceneName, processor.getClass().getName());
        return processor;
    }

    /**
     * @description 获取对话模型
     * @param modelName modelName
     * @return ChatLanguageModel
     */
    private ChatLanguageModel getLanguageModel(String modelName) {
        return this.modelFactory.getModel(modelName);
    }

    /**
     * @description 获取默认对话模型
     * @return ChatLanguageModel
     */
    private ChatLanguageModel getLanguageModel() {
        return this.modelFactory.getDefaultModel();
    }

    /**
     * @description 生成会话id并放入线程对象中
     * @return String
     */
    private String generateSessionId(String sessionId) {
        if (StringUtils.isEmpty(sessionId)) {
            sessionId = UUID.randomUUID().toString();
        }
        sessionHolder.set(sessionId);
        return sessionId;
    }
}
